﻿// 202310312013_cpp_11_17_new_features.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
// https://www.bilibili.com/video/BV1AX4y1u7ez/?spm_id_from=333.999.0.0&vd_source=8bd7b24b38e3e12c558d839b352b32f4
// 注意将项目属性，C++语言标准调整为C++ 17
// C++ 17结构化绑定需要，其他演示特性为C++ 11中的

#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
#include "01.h"
#include "02.h"
#include "04.h"
#include "05.h"
#include "06.h"
#include "07.h"

using namespace std;

int main()
{
    std::cout << "Hello World! cpp 11 17\n";
    
    cout << endl << "01 委托构造函数 Delegating Constructors 01:29 " << endl ;

    cout << endl << "02 类内初始化 In-Class Initializer 01:59" << endl;

    cout << endl << "03 空指针 nullptr 02:21" << endl;
    void demo03();
    demo03();

    cout << endl << "04 枚举类 enum class 03:16" << endl;
    EnumOld1 enumOld1 = FOO;
    // C++11 
    Enum111 enum111 = Enum111::FOO;
    Enum112 enum112 = Enum112::BAR;
    int numOld = FOO;// OK
    // int num11 = Enum111::BAR;// 编译器直接报错
    
    cout << endl << "05 类型推导 Type Inference 03:49" << endl;
    void demo05();
    demo05();
    cout << addAutoReturnCPP14(1, 2.0) << endl;
    cout << addAutoReturnCPP14Before(1, 2.0) << endl;

    cout << endl << "06 常量表达式 constexpr 05:11" << endl;
    //int a05111[powCPP11(2, 3)] = {0};// 编译器报错。问题：这个跟于仕琪老师给出的数组例子怎么就不一样了，那个例子中数组大小明明是变量。TODO
    int a05112[powCPP11Constexpr(2, 3)] = { 0 };// 用来把计算提前到编译时。编译器不报错。 debug一下看看。
    constexpr int a05113 = (1 + 2) * 3;// constexpr可以修饰任何表达式，甚至是一个递归函数。
    constexpr int a05114 = a05113 + 3;

    cout << endl << "07 初始化列表 Initializer List 06:16" << endl;

    cout << endl << "08 基于范围的for循环 Range-Based For-Loops 06:44" << endl;// 另可见函数demo05()
    void demo08();
    demo08();

    cout << endl << "09 智能指针 unique_ptr 07:06" << endl;
    void demo09();
    demo09();

    cout << endl << "10 Lambda表达式 09:30" << endl;
    void demo10();
    demo10();

    cout << endl << endl;

    return 0;
}


// 03 空指针
void demo03func(char* s) {
    cout << "demo03func(char* s)" << endl;
}

void demo03func(int i) {
    cout << "demo03func(int i)" << endl;
}

void demo03() {
    
    char* s = NULL;
    int i = NULL;// 这是不恰当的
    float f = NULL;// 这是不恰当的
    demo03func(NULL);// 这样做有歧义, 但编译并不会报错！

    // C++ 11 推荐空指针写法
    char* s11 = nullptr;
    //int i11 = nullptr;// 编译器报错
    //float f11 = nullptr;// 编译器报错
    demo03func(nullptr);// 明确调用到重载函数demo03func(char* s) 
}

// 05 类型推导
// auto 做函数返回值的例子见05.h
void demo05() {
    // 这里实际展示了以下特性
    // 1. auto这个关键字是C++ 11 里面添加的新特性
    // 2. 初始化列表 Initializer List 见07.h
    // 3. 基于范围的for循环
    // 4. 结构化绑定（C++ 17）
    
    vector<pair<int, int>> numbers = { {1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5} };// 这个容器初始化列表也是C++ 11的特性。

    cout << "传统" << endl;
    for (vector<pair<int, int>>::const_iterator it = numbers.cbegin(); it != numbers.cend(); ++it) {
        cout << it->first << ", " << it->second << endl;
    }

    cout << "C++ 11" << endl;
    for (auto p : numbers) {
        cout << p.first << ", " << p.second << endl;
    }

    cout << "C++ 17" << endl;
    for (auto [key, val] : numbers) {// C++ 17
        cout << key << ", " << val << endl;
    }

    /*
        注：JDK10+ var
        HashMap<String, Integer> map = new HashMap<>();
        for (var entry : map.entrySet()) {
            // foo
        }
    */
}

// 08 基于范围的for循环 Range-Based For-Loops 06:44
// Java::for (Integer i : integers) {}
void demo08() {
    map<int, string> numMap = { {1, "one"}, {2, "two"}, {3, "three"}};

    cout << "传统" << endl;
    for (map<int, string>::iterator it = numMap.begin(); it != numMap.end(); ++it) {
        cout << it->first << ", " << it->second << endl;
    }

    cout << "C++ 17" << endl;
    for (auto [key, val] : numMap) {
        cout << key << ", " << val << endl;
    }

}

// 09 智能指针 unique_ptr 07:06
void demo09() {
    // C++ 98就有了智能指针auto_ptr，但设计有缺陷，C++ 11的时候弃用，C++17中移除。

    struct SomeData {
        int a = 0, b = 0, c = 0;
        //int a, b, c;
    };

    // //////////////////////////////////////////////////////
    SomeData* dataOld = new SomeData;// 传统写法
    delete dataOld;// 需要释放

    // //////////////////////////////////////////////////////
    // C++ 11 unique_ptr 样式1
    std::unique_ptr<SomeData> dataC11Type1(new SomeData);
    dataC11Type1->a = 1;
    dataC11Type1->b = 1;
    dataC11Type1->c = 1;
    // 不需要手动销毁对象

    // //////////////////////////////////////////////////////
    // C++ 11 unique_ptr 样式2（推荐）
    auto dataC11Type2 = std::make_unique<SomeData>();// 这种写法就算构造函数抛出异常也不会得到一个野指针。
    dataC11Type2->a = 1;
    dataC11Type2->b = 1;
    dataC11Type2->c = 1;
    // 不需要手动销毁对象


    // 注：
    // 如果需要将指针在不同的函数间传递，或者需要多个指针指向同一个对象的智能指针
    // 请使用shared_prt
}

// 10 Lambda表达式 09:30
void demo10() {
    // 操作：找出集合中的第一个偶数

    vector<int> nums = {1, 2, 3, 4, 5};
    auto it = find_if(nums.begin(), nums.end(), [](int x) { return x % 2 == 0; });
    cout << "The first even number is " << *it;
}


// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件
